﻿/**************************************************************
 * JSLoader - JavaScript on Loading Demand - jsloader.sourceforge.net
 *
 * Copyright (c) 2007, Morgan Stanley & Co. Incorporated
 *
 * THE FOLLOWING DISCLAIMER APPLIES TO ALL SOFTWARE CODE AND OTHER
 * MATERIALS CONTRIBUTED IN CONNECTION WITH JSLoader:
 *
 * THIS SOFTWARE IS LICENSED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED AND ANY WARRANTY OF NON-
 * INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE MAY
 * BE REDISTRIBUTED TO OTHERS ONLY BY EFFECTIVELY USING THIS OR ANOTHER
 * EQUIVALENT DISCLAIMER AS WELL AS ANY OTHER LICENSE TERMS THAT MAY APPLY.
 *
 * -------------------------------------------------------------
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * You can also find a copy of the GNU Lesser Public License at
 * <http://www.gnu.org/licenses/>.
 * 
 ***********************************************************/

/*
 * @fileoverview JSLoaderEnvironment is intended to model a simplified
 * runtime environment for loading other web functionality, such 
 * as RIA/JavaScript files onto the page. Example code:
 * <pre>
 *    @lt;script src="path/to/jsloader.js"&gt;&lt;/script&gt;
 *    @lt;script type="text/javascript"&gt;
 *       // JSLoader is already defined by including jsloader.js
 *       JSLoader.load("meta","project","release");
 *    &lt;/script&gt;
 * </pre>
 */

/** 
 * Construct a new JSLoaderEnvironment instance
 * @class JSLoaderEnvironment is the class designed to be a 
 * representation of a unix-like shell environment.
 * The purpose of this is to allow the infrastructure to abstract
 * away any library file location knowledge for rapid and enterprise
 * adoption and deployment
 */
function JSLoaderEnvironment(){
  
  // Default
  this.prefix="/assets/";

  // Auto-discover location
  var _remote=false;
  var s=0;
  var _script_tags=document.getElementsByTagName("script");
  var endsWith=function(str, substr){
    return (str && str.indexOf(substr) == (str.length-substr.length));
  };
  for(s=0;s<_script_tags.length;++s){ 
    var src=_script_tags[s].src;
    var src_orig=src;
    if(src){
      if(src.indexOf("://")>-1)
	{
	  src=src.substring(src.indexOf("://")+3);
	  src=src.substring(src.indexOf("/"));
	}
      if(endsWith(src,"jsloader.js")  || endsWith(src,"jsloader-debug.js")) {
		// If the domain is remote, assume we're running in hosted mode
		_remote=(src_orig.indexOf(document.domain)==-1);
		if(_remote) src=src_orig;
	  
	  this.prefix=src.substring(0, src.lastIndexOf("/")+1);
      }
    }
  }
   
  /**
   * @private
   */
  this.suffix=".js";

  /**
   * @private
   * Make the Path of a module to meta/proj/release
   */
  this.makeJSLoaderPath=function(m,p,r,suff){
    // if just a url is specified, use it
    if(!p && !r) return this.stripExternalRef(m);

    // build the m/p/r path
    return this.prefix+m+"/"+p+"/incr/versions/"+r+ ((suff)?this.suffix:"");
  }

  /**
   * The generate the path prefix for a MPR linked into the JSLoader Environmentiables
   * @param m meta
   * @param p project
   * @param r release
   */
  this.makePath=function(m,p,r){
    // if just a url is specified, use it
    if(!p && !r) return this.stripExternalRef(m);

    // build the m/p/r path
    return this.prefix + m +"/" + p + "/" + r + "/";
  }

  /**
   * @private
   */
  this.env=new Object();

  /**
   * @private
   */
  this.loaders=new Object();

  /**
   * The sets an environment variable (make sure it's safe for JS Object[key] notation)
   * The idea here is that modules could set this, and pages which load the module
   * can then get the Environment variables
   * @param k javascript object[key] 
   * @param v value (technically could be of any type...)
   */
  this.setEnv=function(k,v){ 
    this.env[k]=v;
  }

  /**
   * The gets an environment variable previously set
   * @param k javascript object[key] 
   * @returns the value set for this key
   */
  this.getEnv=function(k){ return this.env[k];}

  /**
   * Lists all modules 
   * loaded in this environment.
   * @private
   */
  this._loadedJSLoaders=new Object();
        
  /**
   * This makes a normalized key to stick into loaded_modules and verify if things are loaded.
   * @private
   */
  this.normalize=function(m,p,r){ return (m+"__"+p+"__"+r).toLowerCase();};

  /**
   * This checks whether the given meta/project/release is already loaded.
   * @param m metaproject (or the path of a JS file, if no other args are passed)
   * @param p project
   * @param r release
   * @type boolean
   * @returns Whether m/p/r is loaded
   */
  this.isLoaded=function(m,p,r){
    var xkey=this.normalize(m,p,r);
    return(this._loadedJSLoaders[xkey]!=null);
  };


  /**
   * Gets a "loader" based on the MPR specified in the arguments
   * This is useful for loading subpackages.  You can call {@link JSSubLoader#load} or {@link JSSubLoader#loadAll} on this
   * and it will load submodules under a folder with the "release" number
   * @see JSSubLoader
   * @param m metaproject 
   * @param p project
   * @param r release
   * @returns void
   */
  this.getLoader=function(m,p,r){
    var key=this.normalize(m,p,r);
    var loader=this.loaders[key];
    if(loader) {
      return loader;
    }
    else {
      loader=new JSSubLoader(this,this.makeJSLoaderPath(m,p,r,false)+"/");
      var __path=this.makePath(m,p,r);
      this.setEnv(p.toUpperCase()+"_PATH",__path);
      this.loaders[key]=loader;
      return loader;
    }
  }
  
  /**
   * Loads the requested module into the environment
   * You can also load your own module by calling loadJavascript(url) if you want
   * @param m metaproject 
   * @param p project
   * @param r release
   * @type boolean
   * @returns void
   */
  this.load=function(m,p,r){
    var key=this.normalize(m,p,r);
    var url=this.makeJSLoaderPath(m,p,r,true); 
    try{
      if(this.isLoaded(m,p,r)) {
	return;
      }
      this.loadJavaScript(url);
      this._loadedJSLoaders[key]="true";
    } catch (e){ this.handleError(e); }
  };


  /**
   * Loads a JavaScript file into the page 
   * @param {String} url the url of the javascript file
   */
  this.loadJavaScript=function (url){
    url = this.stripExternalRef(url);
    document.writeln("<scri"+"pt src='"+url+"' type='text/javascript'></sc"+"ript>");
  };

  /**
   * Loads a JavaScript file into the page 
   * @param {String} url the url of the javascript file
   */
  this.loadStyleSheet=function(url){
    url = this.stripExternalRef(url);
    document.writeln("<li"+"nk rel='stylesheet' href='"+url+"' type='text/css'></li"+"nk>");
  };


  /**
   * Strips out any double slashes, double dots, or cross domain references.
   * @param s string
   */
  this.stripExternalRef=function(s){
    var exprs = [/\.\.+/g,/\/\/+/g,/\\\\+/g,/\:+/g,/\'+/g,/\%+/g];
    
    // If it's hosted, we relax the protocol related regex
    exprs = [/\.\.+/g,/\\\\+/g,/\'+/g,/\%+/g];
    
    if (_remote)
    
    for(var i=0; i<exprs.length; i++)
      {
	s = s.replace(exprs[i], '');
      }

    return s;
  }

  /**
   *  Overwritable error handler
   */
  this.handleError=function(e) {
  }
 
  return this;
};

/** 
 * Construct a new JSSubLoader instance. You shoudl never need to call this, as {@link JSLoaderEnvironment#getLoader} gets you one of these from the environment.
 * @class JSSubLoader is designed to load "sub" modules
 * This is a wrapper which is produced when you call  {@link JSLoaderEnvironment#getLoader} 
 * It is designed to allow sub-packages within a given MPR to be loaded easily. This is constructed by JSLoader.getLoader() call
 * so you should never really need to construct one of these.
 * @constructor
 * @param {JSLoaderEnvironment} env_  
 * @param {String} prefix_  The path underneath which the submodules reside
 * 
 */

function JSSubLoader(env_, prefix_){
  /**
   * @private
   */
  this.environment=env_;

  /**
   * @private
   */
  this.prefix=prefix_;

  /**
   * @private
   */

  this.loaded=new Object();

  /**
   * @private
   */
  this.normalize=function(str){ return str.toLowerCase(); }
  
  /**
   * Loads an array of subpackages
   * @param {Array} pkgs an array of packages.
   */

  this.loadAll=function(pkgs_){
    for(i=0;i<pkgs_.length;++i) this.load(pkgs_[i]);
  };

  /**
   * Loads a subpackage, if it's not already loaded
   * @param {String} url the url of the sub-package module file (m/p/r/submodule.js)
   */
  this.load=function(pkg){
    var p=this.normalize(pkg);
    if (this.loaded[p]) {
      return;
    }
    this.loaded[p]=pkg;
    this.environment.loadJavaScript(prefix_+pkg+".js");
  };
};

JSLoader = new JSLoaderEnvironment();

//  LocalWords:  fileoverview

//JSLoader.loadJavaScript(""); 